/*****************************************************************************/
/*                                                                           */
/*                                 stdfunc.c                                 */
/*                                                                           */
/*         Handle inlining of known functions for the cc65 compiler          */
/*                                                                           */
/*                                                                           */
/*                                                                           */
/* (C) 1998-2010 Ullrich von Bassewitz                                       */
/*               Roemerstrasse 52                                            */
/*               D-70794 Filderstadt                                         */
/* EMail:        uz@cc65.org                                                 */
/*                                                                           */
/*                                                                           */
/* This software is provided 'as-is', without any expressed or implied       */
/* warranty.  In no event will the authors be held liable for any damages    */
/* arising from the use of this software.                                    */
/*                                                                           */
/* Permission is granted to anyone to use this software for any purpose,     */
/* including commercial applications, and to alter it and redistribute it    */
/* freely, subject to the following restrictions:                            */
/*                                                                           */
/* 1. The origin of this software must not be misrepresented; you must not   */
/*    claim that you wrote the original software. If you use this software   */
/*    in a product, an acknowledgment in the product documentation would be  */
/*    appreciated but is not required.                                       */
/* 2. Altered source versions must be plainly marked as such, and must not   */
/*    be misrepresented as being the original software.                      */
/* 3. This notice may not be removed or altered from any source              */
/*    distribution.                                                          */
/*                                                                           */
/*****************************************************************************/

#include <stdlib.h>
#include <string.h>

/* common */
#include "attrib.h"
#include "check.h"

/* cc65 */
#include "asmcode.h"
#include "asmlabel.h"
#include "codegen.h"
#include "error.h"
#include "funcdesc.h"
#include "global.h"
#include "litpool.h"
#include "loadexpr.h"
#include "scanner.h"
#include "stackptr.h"
#include "stdfunc.h"
#include "stdnames.h"
#include "typeconv.h"

/*****************************************************************************/
/*                             Function forwards                             */
/*****************************************************************************/

static void StdFunc_memcpy(FuncDesc *, ExprDesc *);
static void StdFunc_memset(FuncDesc *, ExprDesc *);
static void StdFunc_strcmp(FuncDesc *, ExprDesc *);
static void StdFunc_strcpy(FuncDesc *, ExprDesc *);
static void StdFunc_strlen(FuncDesc *, ExprDesc *);

/*****************************************************************************/
/*                                   Data                                    */
/*****************************************************************************/

/* Table with all known functions and their handlers. Must be sorted
** alphabetically!
*/
static struct StdFuncDesc {
  const char *Name;
  void (*Handler)(FuncDesc *, ExprDesc *);
} StdFuncs[] = {
    {"memcpy", StdFunc_memcpy}, {"memset", StdFunc_memset}, {"strcmp", StdFunc_strcmp},
    {"strcpy", StdFunc_strcpy}, {"strlen", StdFunc_strlen},

};
#define FUNC_COUNT (sizeof(StdFuncs) / sizeof(StdFuncs[0]))

typedef struct ArgDesc ArgDesc;
struct ArgDesc {
  const Type *ArgType; /* Required argument type */
  ExprDesc Expr;       /* Argument expression */
  const Type *Type;    /* The original type before conversion */
  CodeMark Load;       /* Start of argument load code */
  CodeMark Push;       /* Start of argument push code */
  CodeMark End;        /* End of the code for calculation+push */
  unsigned Flags;      /* Code generation flags */
};

/*****************************************************************************/
/*                             Helper functions                              */
/*****************************************************************************/

static int CmpFunc(const void *Key, const void *Elem)
/* Compare function for bsearch */
{
  return strcmp((const char *)Key, ((const struct StdFuncDesc *)Elem)->Name);
}

static long ArrayElementCount(const ArgDesc *Arg)
/* Check if the type of the given argument is an array. If so, and if the
** element count is known, return it. In all other cases, return UNSPECIFIED.
*/
{
  long Count;

  if (IsTypeArray(Arg->Type)) {
    Count = GetElementCount(Arg->Type);
    if (Count == FLEXIBLE) {
      /* Treat as unknown */
      Count = UNSPECIFIED;
    }
  } else {
    Count = UNSPECIFIED;
  }
  return Count;
}

static void ParseArg(ArgDesc *Arg, Type *Type)
/* Parse one argument but do not push it onto the stack. Make all fields in
** Arg valid.
*/
{
  /* We have a prototype, so chars may be pushed as chars */
  Arg->Flags = CF_FORCECHAR;

  /* Remember the required argument type */
  Arg->ArgType = Type;

  /* Read the expression we're going to pass to the function */
  MarkedExprWithCheck(hie1, &Arg->Expr);

  /* Remember the actual argument type */
  Arg->Type = Arg->Expr.Type;

  /* Convert this expression to the expected type */
  TypeConversion(&Arg->Expr, Type);

  /* Remember the following code position */
  GetCodePos(&Arg->Load);

  /* If the value is a constant, set the flag, otherwise load it into the
  ** primary register.
  */
  if (ED_IsConstAbsInt(&Arg->Expr) && ED_CodeRangeIsEmpty(&Arg->Expr)) {
    /* Remember that we have a constant value */
    Arg->Flags |= CF_CONST;
  } else {
    /* Load into the primary */
    LoadExpr(CF_NONE, &Arg->Expr);
  }

  /* Remember the following code position */
  GetCodePos(&Arg->Push);
  GetCodePos(&Arg->End);

  /* Use the type of the argument for the push */
  Arg->Flags |= TypeOf(Arg->Expr.Type);
}

void AddCmpCodeIfSizeNot256(const char *Code, long Size)
/* Add a line of Assembly code that compares an index register
** only if it isn't comparing to #<256.  (If the next line
** is "bne", then this will avoid a redundant line.)
*/
{
  if (Size != 256) {
    AddCodeLine(Code, (unsigned int)Size);
  }
}

/*****************************************************************************/
/*                                  memcpy                                   */
/*****************************************************************************/

static void StdFunc_memcpy(FuncDesc *F attribute((unused)), ExprDesc *Expr)
/* Handle the memcpy function */
{
  /* Argument types: (void*, const void*, size_t) */
  static Type Arg1Type[] = {TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END)};
  static Type Arg2Type[] = {TYPE(T_PTR), TYPE(T_VOID | T_QUAL_CONST), TYPE(T_END)};
  static Type Arg3Type[] = {TYPE(T_SIZE_T), TYPE(T_END)};

  ArgDesc Arg1, Arg2, Arg3;
  unsigned ParamSize = 0;
  unsigned Label;
  int Offs;

  /* Argument #1 */
  ParseArg(&Arg1, Arg1Type);
  g_push(Arg1.Flags, Arg1.Expr.IVal);
  GetCodePos(&Arg1.End);
  ParamSize += SizeOf(Arg1Type);
  ConsumeComma();

  /* Argument #2 */
  ParseArg(&Arg2, Arg2Type);
  g_push(Arg2.Flags, Arg2.Expr.IVal);
  GetCodePos(&Arg2.End);
  ParamSize += SizeOf(Arg2Type);
  ConsumeComma();

  /* Argument #3. Since memcpy is a fastcall function, we must load the
  ** arg into the primary if it is not already there. This parameter is
  ** also ignored for the calculation of the parameter size, since it is
  ** not passed via the stack.
  */
  ParseArg(&Arg3, Arg3Type);
  if (Arg3.Flags & CF_CONST) {
    LoadExpr(CF_NONE, &Arg3.Expr);
  }

  /* Emit the actual function call. This will also cleanup the stack. */
  g_call(CF_FIXARGC, Func_memcpy, ParamSize);

  if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal == 0) {

    /* memcpy has been called with a count argument of zero */
    Warning("Call to memcpy has no effect");

    /* Remove all of the generated code but the load of the first
    ** argument, which is what memcpy returns.
    */
    RemoveCode(&Arg1.Push);

    /* Set the function result to the first argument */
    *Expr = Arg1.Expr;

    /* Bail out, no need for further improvements */
    goto ExitPoint;
  }

  if (IS_Get(&InlineStdFuncs)) {

    /* We've generated the complete code for the function now and know the
    ** types of all parameters. Check for situations where better code can
    ** be generated. If such a situation is detected, throw away the
    ** generated, and emit better code.
    */
    if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
        ((ED_IsRVal(&Arg2.Expr) && ED_IsLocConst(&Arg2.Expr)) ||
         (ED_IsLVal(&Arg2.Expr) && ED_IsLocRegister(&Arg2.Expr))) &&
        ((ED_IsRVal(&Arg1.Expr) && ED_IsLocConst(&Arg1.Expr)) ||
         (ED_IsLVal(&Arg1.Expr) && ED_IsLocRegister(&Arg1.Expr)))) {

      int Reg1 = ED_IsLVal(&Arg1.Expr) && ED_IsLocRegister(&Arg1.Expr);
      int Reg2 = ED_IsLVal(&Arg2.Expr) && ED_IsLocRegister(&Arg2.Expr);

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need a label */
      Label = GetLocalLabel();

      /* Generate memcpy code */
      if (Arg3.Expr.IVal <= 129) {

        AddCodeLine("ldy #$%02X", (unsigned char)(Arg3.Expr.IVal - 1));
        g_defcodelabel(Label);
        if (Reg2) {
          AddCodeLine("lda (%s),y", ED_GetLabelName(&Arg2.Expr, 0));
        } else {
          AddCodeLine("lda %s,y", ED_GetLabelName(&Arg2.Expr, 0));
        }
        if (Reg1) {
          AddCodeLine("sta (%s),y", ED_GetLabelName(&Arg1.Expr, 0));
        } else {
          AddCodeLine("sta %s,y", ED_GetLabelName(&Arg1.Expr, 0));
        }
        AddCodeLine("dey");
        AddCodeLine("bpl %s", LocalLabelName(Label));

      } else {

        AddCodeLine("ldy #$00");
        g_defcodelabel(Label);
        if (Reg2) {
          AddCodeLine("lda (%s),y", ED_GetLabelName(&Arg2.Expr, 0));
        } else {
          AddCodeLine("lda %s,y", ED_GetLabelName(&Arg2.Expr, 0));
        }
        if (Reg1) {
          AddCodeLine("sta (%s),y", ED_GetLabelName(&Arg1.Expr, 0));
        } else {
          AddCodeLine("sta %s,y", ED_GetLabelName(&Arg1.Expr, 0));
        }
        AddCodeLine("iny");
        AddCmpCodeIfSizeNot256("cpy #$%02X", Arg3.Expr.IVal);
        AddCodeLine("bne %s", LocalLabelName(Label));
      }

      /* memcpy returns the address, so the result is actually identical
      ** to the first argument.
      */
      *Expr = Arg1.Expr;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsRVal(&Arg2.Expr) && ED_IsLocConst(&Arg2.Expr) &&
        ED_IsRVal(&Arg1.Expr) && ED_IsLocStack(&Arg1.Expr) && (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {

      /* It is possible to just use one index register even if the stack
      ** offset is not zero, by adjusting the offset to the constant
      ** address accordingly. But we cannot do this if the data in
      ** question is in the register space or at an absolute address less
      ** than 256. Register space is zero page, which means that the
      ** address calculation could overflow in the linker.
      */
      int AllowOneIndex = !ED_IsLocRegister(&Arg2.Expr) && !(ED_IsLocAbs(&Arg2.Expr) && Arg2.Expr.IVal < 256);

      /* Calculate the real stack offset */
      Offs = ED_GetStackOffs(&Arg1.Expr, 0);

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need a label */
      Label = GetLocalLabel();

      /* Generate memcpy code */
      if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) {

        if (Offs == 0) {
          AddCodeLine("ldy #$%02X", (unsigned char)(Offs + Arg3.Expr.IVal - 1));
          g_defcodelabel(Label);
          AddCodeLine("lda %s,y", ED_GetLabelName(&Arg2.Expr, -Offs));
          AddCodeLine("sta (sp),y");
          AddCodeLine("dey");
          AddCodeLine("bpl %s", LocalLabelName(Label));
        } else {
          AddCodeLine("ldx #$%02X", (unsigned char)(Arg3.Expr.IVal - 1));
          AddCodeLine("ldy #$%02X", (unsigned char)(Offs + Arg3.Expr.IVal - 1));
          g_defcodelabel(Label);
          AddCodeLine("lda %s,x", ED_GetLabelName(&Arg2.Expr, 0));
          AddCodeLine("sta (sp),y");
          AddCodeLine("dey");
          AddCodeLine("dex");
          AddCodeLine("bpl %s", LocalLabelName(Label));
        }

      } else {

        if (Offs == 0 || AllowOneIndex) {
          AddCodeLine("ldy #$%02X", (unsigned char)Offs);
          g_defcodelabel(Label);
          AddCodeLine("lda %s,y", ED_GetLabelName(&Arg2.Expr, -Offs));
          AddCodeLine("sta (sp),y");
          AddCodeLine("iny");
          AddCmpCodeIfSizeNot256("cpy #$%02X", Offs + Arg3.Expr.IVal);
          AddCodeLine("bne %s", LocalLabelName(Label));
        } else {
          AddCodeLine("ldx #$00");
          AddCodeLine("ldy #$%02X", (unsigned char)Offs);
          g_defcodelabel(Label);
          AddCodeLine("lda %s,x", ED_GetLabelName(&Arg2.Expr, 0));
          AddCodeLine("sta (sp),y");
          AddCodeLine("iny");
          AddCodeLine("inx");
          AddCmpCodeIfSizeNot256("cpx #$%02X", Arg3.Expr.IVal);
          AddCodeLine("bne %s", LocalLabelName(Label));
        }
      }

      /* memcpy returns the address, so the result is actually identical
      ** to the first argument.
      */
      *Expr = Arg1.Expr;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsRVal(&Arg2.Expr) && ED_IsLocStack(&Arg2.Expr) &&
        (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 && ED_IsRVal(&Arg1.Expr) && ED_IsLocConst(&Arg1.Expr)) {

      /* It is possible to just use one index register even if the stack
      ** offset is not zero, by adjusting the offset to the constant
      ** address accordingly. But we cannot do this if the data in
      ** question is in the register space or at an absolute address less
      ** than 256. Register space is zero page, which means that the
      ** address calculation could overflow in the linker.
      */
      int AllowOneIndex = !ED_IsLocRegister(&Arg1.Expr) && !(ED_IsLocAbs(&Arg1.Expr) && Arg1.Expr.IVal < 256);

      /* Calculate the real stack offset */
      Offs = ED_GetStackOffs(&Arg2.Expr, 0);

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need a label */
      Label = GetLocalLabel();

      /* Generate memcpy code */
      if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) {

        if (Offs == 0) {
          AddCodeLine("ldy #$%02X", (unsigned char)(Arg3.Expr.IVal - 1));
          g_defcodelabel(Label);
          AddCodeLine("lda (sp),y");
          AddCodeLine("sta %s,y", ED_GetLabelName(&Arg1.Expr, 0));
          AddCodeLine("dey");
          AddCodeLine("bpl %s", LocalLabelName(Label));
        } else {
          AddCodeLine("ldx #$%02X", (unsigned char)(Arg3.Expr.IVal - 1));
          AddCodeLine("ldy #$%02X", (unsigned char)(Offs + Arg3.Expr.IVal - 1));
          g_defcodelabel(Label);
          AddCodeLine("lda (sp),y");
          AddCodeLine("sta %s,x", ED_GetLabelName(&Arg1.Expr, 0));
          AddCodeLine("dey");
          AddCodeLine("dex");
          AddCodeLine("bpl %s", LocalLabelName(Label));
        }

      } else {

        if (Offs == 0 || AllowOneIndex) {
          AddCodeLine("ldy #$%02X", (unsigned char)Offs);
          g_defcodelabel(Label);
          AddCodeLine("lda (sp),y");
          AddCodeLine("sta %s,y", ED_GetLabelName(&Arg1.Expr, -Offs));
          AddCodeLine("iny");
          AddCmpCodeIfSizeNot256("cpy #$%02X", Offs + Arg3.Expr.IVal);
          AddCodeLine("bne %s", LocalLabelName(Label));
        } else {
          AddCodeLine("ldx #$00");
          AddCodeLine("ldy #$%02X", (unsigned char)Offs);
          g_defcodelabel(Label);
          AddCodeLine("lda (sp),y");
          AddCodeLine("sta %s,x", ED_GetLabelName(&Arg1.Expr, 0));
          AddCodeLine("iny");
          AddCodeLine("inx");
          AddCmpCodeIfSizeNot256("cpx #$%02X", Arg3.Expr.IVal);
          AddCodeLine("bne %s", LocalLabelName(Label));
        }
      }

      /* memcpy returns the address, so the result is actually identical
      ** to the first argument.
      */
      *Expr = Arg1.Expr;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsRVal(&Arg2.Expr) && ED_IsLocStack(&Arg2.Expr) &&
        (Offs = ED_GetStackOffs(&Arg2.Expr, 0)) == 0) {

      /* Drop the generated code but leave the load of the first argument*/
      RemoveCode(&Arg1.Push);

      /* We need a label */
      Label = GetLocalLabel();

      /* Generate memcpy code */
      AddCodeLine("sta ptr1");
      AddCodeLine("stx ptr1+1");
      if (Arg3.Expr.IVal <= 129) {
        AddCodeLine("ldy #$%02X", (unsigned char)(Arg3.Expr.IVal - 1));
        g_defcodelabel(Label);
        AddCodeLine("lda (sp),y");
        AddCodeLine("sta (ptr1),y");
        AddCodeLine("dey");
        AddCodeLine("bpl %s", LocalLabelName(Label));
      } else {
        AddCodeLine("ldy #$00");
        g_defcodelabel(Label);
        AddCodeLine("lda (sp),y");
        AddCodeLine("sta (ptr1),y");
        AddCodeLine("iny");
        AddCmpCodeIfSizeNot256("cpy #$%02X", Arg3.Expr.IVal);
        AddCodeLine("bne %s", LocalLabelName(Label));
      }

      /* Reload result - X hasn't changed by the code above */
      AddCodeLine("lda ptr1");

      /* The function result is an rvalue in the primary register */
      ED_MakeRValExpr(Expr);
      Expr->Type = GetFuncReturn(Expr->Type);

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }
  }

  /* The function result is an rvalue in the primary register */
  ED_MakeRValExpr(Expr);
  Expr->Type = GetFuncReturn(Expr->Type);

ExitPoint:
  /* We expect the closing brace */
  ConsumeRParen();
}

/*****************************************************************************/
/*                                  memset                                   */
/*****************************************************************************/

static void StdFunc_memset(FuncDesc *F attribute((unused)), ExprDesc *Expr)
/* Handle the memset function */
{
  /* Argument types: (void*, int, size_t) */
  static Type Arg1Type[] = {TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END)};
  static Type Arg2Type[] = {TYPE(T_INT), TYPE(T_END)};
  static Type Arg3Type[] = {TYPE(T_SIZE_T), TYPE(T_END)};

  ArgDesc Arg1, Arg2, Arg3;
  int MemSet = 1; /* Use real memset if true */
  unsigned ParamSize = 0;
  unsigned Label;

  /* Argument #1 */
  ParseArg(&Arg1, Arg1Type);
  g_push(Arg1.Flags, Arg1.Expr.IVal);
  GetCodePos(&Arg1.End);
  ParamSize += SizeOf(Arg1Type);
  ConsumeComma();

  /* Argument #2. This argument is special in that we will call another
  ** function if it is a constant zero.
  */
  ParseArg(&Arg2, Arg2Type);
  if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
    /* Don't call memset, call bzero instead */
    MemSet = 0;
  } else {
    /* Push the argument */
    g_push(Arg2.Flags, Arg2.Expr.IVal);
    GetCodePos(&Arg2.End);
    ParamSize += SizeOf(Arg2Type);
  }
  ConsumeComma();

  /* Argument #3. Since memset is a fastcall function, we must load the
  ** arg into the primary if it is not already there. This parameter is
  ** also ignored for the calculation of the parameter size, since it is
  ** not passed via the stack.
  */
  ParseArg(&Arg3, Arg3Type);
  if (Arg3.Flags & CF_CONST) {
    LoadExpr(CF_NONE, &Arg3.Expr);
  }

  /* Emit the actual function call. This will also cleanup the stack. */
  g_call(CF_FIXARGC, MemSet ? Func_memset : Func__bzero, ParamSize);

  if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal == 0) {

    /* memset has been called with a count argument of zero */
    Warning("Call to memset has no effect");

    /* Remove all of the generated code but the load of the first
    ** argument, which is what memset returns.
    */
    RemoveCode(&Arg1.Push);

    /* Set the function result to the first argument */
    *Expr = Arg1.Expr;

    /* Bail out, no need for further improvements */
    goto ExitPoint;
  }

  if (IS_Get(&InlineStdFuncs)) {

    /* We've generated the complete code for the function now and know the
    ** types of all parameters. Check for situations where better code can
    ** be generated. If such a situation is detected, throw away the
    ** generated, and emit better code.
    ** Note: Lots of improvements would be possible here, but I will
    ** concentrate on the most common case: memset with arguments 2 and 3
    ** being constant numerical values. Some checks have shown that this
    ** covers nearly 90% of all memset calls.
    */
    if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsConstAbsInt(&Arg2.Expr) &&
        ((ED_IsRVal(&Arg1.Expr) && ED_IsLocConst(&Arg1.Expr)) ||
         (ED_IsLVal(&Arg1.Expr) && ED_IsLocRegister(&Arg1.Expr)))) {

      int Reg = ED_IsLVal(&Arg1.Expr) && ED_IsLocRegister(&Arg1.Expr);

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need a label */
      Label = GetLocalLabel();

      /* Generate memset code */
      if (Arg3.Expr.IVal <= 129) {

        AddCodeLine("ldy #$%02X", (unsigned char)(Arg3.Expr.IVal - 1));
        AddCodeLine("lda #$%02X", (unsigned char)Arg2.Expr.IVal);
        g_defcodelabel(Label);
        if (Reg) {
          AddCodeLine("sta (%s),y", ED_GetLabelName(&Arg1.Expr, 0));
        } else {
          AddCodeLine("sta %s,y", ED_GetLabelName(&Arg1.Expr, 0));
        }
        AddCodeLine("dey");
        AddCodeLine("bpl %s", LocalLabelName(Label));

      } else {

        AddCodeLine("ldy #$00");
        AddCodeLine("lda #$%02X", (unsigned char)Arg2.Expr.IVal);
        g_defcodelabel(Label);
        if (Reg) {
          AddCodeLine("sta (%s),y", ED_GetLabelName(&Arg1.Expr, 0));
        } else {
          AddCodeLine("sta %s,y", ED_GetLabelName(&Arg1.Expr, 0));
        }
        AddCodeLine("iny");
        AddCmpCodeIfSizeNot256("cpy #$%02X", Arg3.Expr.IVal);
        AddCodeLine("bne %s", LocalLabelName(Label));
      }

      /* memset returns the address, so the result is actually identical
      ** to the first argument.
      */
      *Expr = Arg1.Expr;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsConstAbsInt(&Arg2.Expr) &&
        ED_IsRVal(&Arg1.Expr) && ED_IsLocStack(&Arg1.Expr) && (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {

      /* Calculate the real stack offset */
      int Offs = ED_GetStackOffs(&Arg1.Expr, 0);

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need a label */
      Label = GetLocalLabel();

      /* Generate memset code */
      AddCodeLine("ldy #$%02X", (unsigned char)Offs);
      AddCodeLine("lda #$%02X", (unsigned char)Arg2.Expr.IVal);
      g_defcodelabel(Label);
      AddCodeLine("sta (sp),y");
      AddCodeLine("iny");
      AddCmpCodeIfSizeNot256("cpy #$%02X", Offs + Arg3.Expr.IVal);
      AddCodeLine("bne %s", LocalLabelName(Label));

      /* memset returns the address, so the result is actually identical
      ** to the first argument.
      */
      *Expr = Arg1.Expr;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    if (ED_IsConstAbsInt(&Arg3.Expr) && Arg3.Expr.IVal <= 256 && ED_IsConstAbsInt(&Arg2.Expr) &&
        (Arg2.Expr.IVal != 0 || IS_Get(&CodeSizeFactor) > 200)) {

      /* Remove all of the generated code but the load of the first
      ** argument.
      */
      RemoveCode(&Arg1.Push);

      /* We need a label */
      Label = GetLocalLabel();

      /* Generate code */
      AddCodeLine("sta ptr1");
      AddCodeLine("stx ptr1+1");
      if (Arg3.Expr.IVal <= 129) {
        AddCodeLine("ldy #$%02X", (unsigned char)(Arg3.Expr.IVal - 1));
        AddCodeLine("lda #$%02X", (unsigned char)Arg2.Expr.IVal);
        g_defcodelabel(Label);
        AddCodeLine("sta (ptr1),y");
        AddCodeLine("dey");
        AddCodeLine("bpl %s", LocalLabelName(Label));
      } else {
        AddCodeLine("ldy #$00");
        AddCodeLine("lda #$%02X", (unsigned char)Arg2.Expr.IVal);
        g_defcodelabel(Label);
        AddCodeLine("sta (ptr1),y");
        AddCodeLine("iny");
        AddCmpCodeIfSizeNot256("cpy #$%02X", Arg3.Expr.IVal);
        AddCodeLine("bne %s", LocalLabelName(Label));
      }

      /* Load the function result pointer into a/x (x is still valid). This
      ** code will get removed by the optimizer if it is not used later.
      */
      AddCodeLine("lda ptr1");

      /* The function result is an rvalue in the primary register */
      ED_MakeRValExpr(Expr);
      Expr->Type = GetFuncReturn(Expr->Type);

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }
  }

  /* The function result is an rvalue in the primary register */
  ED_MakeRValExpr(Expr);
  Expr->Type = GetFuncReturn(Expr->Type);

ExitPoint:
  /* We expect the closing brace */
  ConsumeRParen();
}

/*****************************************************************************/
/*                                  strcmp                                   */
/*****************************************************************************/

static void StdFunc_strcmp(FuncDesc *F attribute((unused)), ExprDesc *Expr)
/* Handle the strcmp function */
{
  /* Argument types: (const char*, const char*) */
  static Type Arg1Type[] = {TYPE(T_PTR), TYPE(T_CHAR | T_QUAL_CONST), TYPE(T_END)};
  static Type Arg2Type[] = {TYPE(T_PTR), TYPE(T_CHAR | T_QUAL_CONST), TYPE(T_END)};

  ArgDesc Arg1, Arg2;
  unsigned ParamSize = 0;
  long ECount1;
  long ECount2;
  int IsArray;
  int Offs;

  /* Setup the argument type string */
  Arg1Type[1].C = GetDefaultChar() | T_QUAL_CONST;
  Arg2Type[1].C = GetDefaultChar() | T_QUAL_CONST;

  /* Argument #1 */
  ParseArg(&Arg1, Arg1Type);
  g_push(Arg1.Flags, Arg1.Expr.IVal);
  ParamSize += SizeOf(Arg1Type);
  ConsumeComma();

  /* Argument #2. */
  ParseArg(&Arg2, Arg2Type);

  /* Since strcmp is a fastcall function, we must load the
  ** arg into the primary if it is not already there. This parameter is
  ** also ignored for the calculation of the parameter size, since it is
  ** not passed via the stack.
  */
  if (Arg2.Flags & CF_CONST) {
    LoadExpr(CF_NONE, &Arg2.Expr);
  }

  /* Emit the actual function call. This will also cleanup the stack. */
  g_call(CF_FIXARGC, Func_strcmp, ParamSize);

  /* Get the element counts of the arguments. Then get the larger of the
  ** two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
  */
  ECount1 = ArrayElementCount(&Arg1);
  ECount2 = ArrayElementCount(&Arg2);
  if (ECount2 > ECount1) {
    ECount1 = ECount2;
  }

  if (IS_Get(&InlineStdFuncs)) {

    /* If the second argument is the empty string literal, we can generate
    ** more efficient code.
    */
    if (ED_IsLocLiteral(&Arg2.Expr) && IS_Get(&WritableStrings) == 0 && GetLiteralSize(Arg2.Expr.LVal) == 1 &&
        GetLiteralStr(Arg2.Expr.LVal)[0] == '\0') {

      /* Drop the generated code so we have the first argument in the
      ** primary
      */
      RemoveCode(&Arg1.Push);

      /* We don't need the literal any longer */
      ReleaseLiteral(Arg2.Expr.LVal);

      /* We do now have Arg1 in the primary. Load the first character from
      ** this string and cast to int. This is the function result.
      */
      IsArray = IsTypeArray(Arg1.Type) && ED_IsRVal(&Arg1.Expr);
      if (IsArray && ED_IsLocStack(&Arg1.Expr) && (Offs = ED_GetStackOffs(&Arg1.Expr, 0) < 256)) {
        /* Drop the generated code */
        RemoveCode(&Arg1.Load);

        /* Generate code */
        AddCodeLine("ldy #$%02X", Offs);
        AddCodeLine("ldx #$00");
        AddCodeLine("lda (sp),y");
      } else if (IsArray && ED_IsLocConst(&Arg1.Expr)) {
        /* Drop the generated code */
        RemoveCode(&Arg1.Load);

        /* Generate code */
        AddCodeLine("ldx #$00");
        AddCodeLine("lda %s", ED_GetLabelName(&Arg1.Expr, 0));
      } else {
        /* Drop part of the generated code so we have the first argument
        ** in the primary
        */
        RemoveCode(&Arg1.Push);

        /* Fetch the first char */
        g_getind(CF_CHAR | CF_UNSIGNED, 0);
      }

    } else if ((IS_Get(&CodeSizeFactor) >= 165) &&
               ((ED_IsRVal(&Arg2.Expr) && ED_IsLocConst(&Arg2.Expr)) ||
                (ED_IsLVal(&Arg2.Expr) && ED_IsLocRegister(&Arg2.Expr))) &&
               ((ED_IsRVal(&Arg1.Expr) && ED_IsLocConst(&Arg1.Expr)) ||
                (ED_IsLVal(&Arg1.Expr) && ED_IsLocRegister(&Arg1.Expr))) &&
               (IS_Get(&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) {

      unsigned Entry, Loop, Fin; /* Labels */
      const char *Load;
      const char *Compare;

      if (ED_IsLVal(&Arg1.Expr) && ED_IsLocRegister(&Arg1.Expr)) {
        Load = "lda (%s),y";
      } else {
        Load = "lda %s,y";
      }
      if (ED_IsLVal(&Arg2.Expr) && ED_IsLocRegister(&Arg2.Expr)) {
        Compare = "cmp (%s),y";
      } else {
        Compare = "cmp %s,y";
      }

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need labels */
      Entry = GetLocalLabel();
      Loop = GetLocalLabel();
      Fin = GetLocalLabel();

      /* Generate strcmp code */
      AddCodeLine("ldy #$00");
      AddCodeLine("beq %s", LocalLabelName(Entry));
      g_defcodelabel(Loop);
      AddCodeLine("tax");
      AddCodeLine("beq %s", LocalLabelName(Fin));
      AddCodeLine("iny");
      g_defcodelabel(Entry);
      AddCodeLine(Load, ED_GetLabelName(&Arg1.Expr, 0));
      AddCodeLine(Compare, ED_GetLabelName(&Arg2.Expr, 0));
      AddCodeLine("beq %s", LocalLabelName(Loop));
      AddCodeLine("ldx #$01");
      AddCodeLine("bcs %s", LocalLabelName(Fin));
      AddCodeLine("ldx #$FF");
      g_defcodelabel(Fin);

    } else if ((IS_Get(&CodeSizeFactor) > 190) &&
               ((ED_IsRVal(&Arg2.Expr) && ED_IsLocConst(&Arg2.Expr)) ||
                (ED_IsLVal(&Arg2.Expr) && ED_IsLocRegister(&Arg2.Expr))) &&
               (IS_Get(&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) {

      unsigned Entry, Loop, Fin; /* Labels */
      const char *Compare;

      if (ED_IsLVal(&Arg2.Expr) && ED_IsLocRegister(&Arg2.Expr)) {
        Compare = "cmp (%s),y";
      } else {
        Compare = "cmp %s,y";
      }

      /* Drop the generated code */
      RemoveCode(&Arg1.Push);

      /* We need labels */
      Entry = GetLocalLabel();
      Loop = GetLocalLabel();
      Fin = GetLocalLabel();

      /* Store Arg1 into ptr1 */
      AddCodeLine("sta ptr1");
      AddCodeLine("stx ptr1+1");

      /* Generate strcmp code */
      AddCodeLine("ldy #$00");
      AddCodeLine("beq %s", LocalLabelName(Entry));
      g_defcodelabel(Loop);
      AddCodeLine("tax");
      AddCodeLine("beq %s", LocalLabelName(Fin));
      AddCodeLine("iny");
      g_defcodelabel(Entry);
      AddCodeLine("lda (ptr1),y");
      AddCodeLine(Compare, ED_GetLabelName(&Arg2.Expr, 0));
      AddCodeLine("beq %s", LocalLabelName(Loop));
      AddCodeLine("ldx #$01");
      AddCodeLine("bcs %s", LocalLabelName(Fin));
      AddCodeLine("ldx #$FF");
      g_defcodelabel(Fin);
    }
  }

  /* The function result is an rvalue in the primary register */
  ED_MakeRValExpr(Expr);
  Expr->Type = GetFuncReturn(Expr->Type);

  /* We expect the closing brace */
  ConsumeRParen();
}

/*****************************************************************************/
/*                                  strcpy                                   */
/*****************************************************************************/

static void StdFunc_strcpy(FuncDesc *F attribute((unused)), ExprDesc *Expr)
/* Handle the strcpy function */
{
  /* Argument types: (char*, const char*) */
  static Type Arg1Type[] = {TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END)};
  static Type Arg2Type[] = {TYPE(T_PTR), TYPE(T_CHAR | T_QUAL_CONST), TYPE(T_END)};

  ArgDesc Arg1, Arg2;
  unsigned ParamSize = 0;
  long ECount;
  unsigned L1;

  /* Setup the argument type string */
  Arg1Type[1].C = GetDefaultChar();
  Arg2Type[1].C = GetDefaultChar() | T_QUAL_CONST;

  /* Argument #1 */
  ParseArg(&Arg1, Arg1Type);
  g_push(Arg1.Flags, Arg1.Expr.IVal);
  GetCodePos(&Arg1.End);
  ParamSize += SizeOf(Arg1Type);
  ConsumeComma();

  /* Argument #2. Since strcpy is a fastcall function, we must load the
  ** arg into the primary if it is not already there. This parameter is
  ** also ignored for the calculation of the parameter size, since it is
  ** not passed via the stack.
  */
  ParseArg(&Arg2, Arg2Type);
  if (Arg2.Flags & CF_CONST) {
    LoadExpr(CF_NONE, &Arg2.Expr);
  }

  /* Emit the actual function call. This will also cleanup the stack. */
  g_call(CF_FIXARGC, Func_strcpy, ParamSize);

  /* Get the element count of argument 1 if it is an array */
  ECount = ArrayElementCount(&Arg1);

  if (IS_Get(&InlineStdFuncs)) {

    /* We've generated the complete code for the function now and know the
    ** types of all parameters. Check for situations where better code can
    ** be generated. If such a situation is detected, throw away the
    ** generated, and emit better code.
    */
    if (((ED_IsRVal(&Arg2.Expr) && ED_IsLocConst(&Arg2.Expr)) ||
         (ED_IsLVal(&Arg2.Expr) && ED_IsLocRegister(&Arg2.Expr))) &&
        ((ED_IsRVal(&Arg1.Expr) && ED_IsLocConst(&Arg1.Expr)) ||
         (ED_IsLVal(&Arg1.Expr) && ED_IsLocRegister(&Arg1.Expr))) &&
        (IS_Get(&EagerlyInlineFuncs) || (ECount != UNSPECIFIED && ECount < 256))) {

      const char *Load;
      const char *Store;
      if (ED_IsLVal(&Arg2.Expr) && ED_IsLocRegister(&Arg2.Expr)) {
        Load = "lda (%s),y";
      } else {
        Load = "lda %s,y";
      }
      if (ED_IsLVal(&Arg1.Expr) && ED_IsLocRegister(&Arg1.Expr)) {
        Store = "sta (%s),y";
      } else {
        Store = "sta %s,y";
      }

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need labels */
      L1 = GetLocalLabel();

      /* Generate strcpy code */
      AddCodeLine("ldy #$FF");
      g_defcodelabel(L1);
      AddCodeLine("iny");
      AddCodeLine(Load, ED_GetLabelName(&Arg2.Expr, 0));
      AddCodeLine(Store, ED_GetLabelName(&Arg1.Expr, 0));
      AddCodeLine("bne %s", LocalLabelName(L1));

      /* strcpy returns argument #1 */
      *Expr = Arg1.Expr;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    if (ED_IsRVal(&Arg2.Expr) && ED_IsLocStack(&Arg2.Expr) && StackPtr >= -255 && ED_IsRVal(&Arg1.Expr) &&
        ED_IsLocConst(&Arg1.Expr)) {

      /* It is possible to just use one index register even if the stack
      ** offset is not zero, by adjusting the offset to the constant
      ** address accordingly. But we cannot do this if the data in
      ** question is in the register space or at an absolute address less
      ** than 256. Register space is zero page, which means that the
      ** address calculation could overflow in the linker.
      */
      int AllowOneIndex = !ED_IsLocRegister(&Arg1.Expr) && !(ED_IsLocAbs(&Arg1.Expr) && Arg1.Expr.IVal < 256);

      /* Calculate the real stack offset */
      int Offs = ED_GetStackOffs(&Arg2.Expr, 0);

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need labels */
      L1 = GetLocalLabel();

      /* Generate strcpy code */
      AddCodeLine("ldy #$%02X", (unsigned char)(Offs - 1));
      if (Offs == 0 || AllowOneIndex) {
        g_defcodelabel(L1);
        AddCodeLine("iny");
        AddCodeLine("lda (sp),y");
        AddCodeLine("sta %s,y", ED_GetLabelName(&Arg1.Expr, -Offs));
      } else {
        AddCodeLine("ldx #$FF");
        g_defcodelabel(L1);
        AddCodeLine("iny");
        AddCodeLine("inx");
        AddCodeLine("lda (sp),y");
        AddCodeLine("sta %s,x", ED_GetLabelName(&Arg1.Expr, 0));
      }
      AddCodeLine("bne %s", LocalLabelName(L1));

      /* strcpy returns argument #1 */
      *Expr = Arg1.Expr;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    if (ED_IsRVal(&Arg2.Expr) && ED_IsLocConst(&Arg2.Expr) && ED_IsRVal(&Arg1.Expr) && ED_IsLocStack(&Arg1.Expr) &&
        StackPtr >= -255) {

      /* It is possible to just use one index register even if the stack
      ** offset is not zero, by adjusting the offset to the constant
      ** address accordingly. But we cannot do this if the data in
      ** question is in the register space or at an absolute address less
      ** than 256. Register space is zero page, which means that the
      ** address calculation could overflow in the linker.
      */
      int AllowOneIndex = !ED_IsLocRegister(&Arg2.Expr) && !(ED_IsLocAbs(&Arg2.Expr) && Arg2.Expr.IVal < 256);

      /* Calculate the real stack offset */
      int Offs = ED_GetStackOffs(&Arg1.Expr, 0);

      /* Drop the generated code */
      RemoveCode(&Arg1.Expr.Start);

      /* We need labels */
      L1 = GetLocalLabel();

      /* Generate strcpy code */
      AddCodeLine("ldy #$%02X", (unsigned char)(Offs - 1));
      if (Offs == 0 || AllowOneIndex) {
        g_defcodelabel(L1);
        AddCodeLine("iny");
        AddCodeLine("lda %s,y", ED_GetLabelName(&Arg2.Expr, -Offs));
        AddCodeLine("sta (sp),y");
      } else {
        AddCodeLine("ldx #$FF");
        g_defcodelabel(L1);
        AddCodeLine("iny");
        AddCodeLine("inx");
        AddCodeLine("lda %s,x", ED_GetLabelName(&Arg2.Expr, 0));
        AddCodeLine("sta (sp),y");
      }
      AddCodeLine("bne %s", LocalLabelName(L1));

      /* strcpy returns argument #1 */
      *Expr = Arg1.Expr;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }
  }

  /* The function result is an rvalue in the primary register */
  ED_MakeRValExpr(Expr);
  Expr->Type = GetFuncReturn(Expr->Type);

ExitPoint:
  /* We expect the closing brace */
  ConsumeRParen();
}

/*****************************************************************************/
/*                                  strlen                                   */
/*****************************************************************************/

static void StdFunc_strlen(FuncDesc *F attribute((unused)), ExprDesc *Expr)
/* Handle the strlen function */
{
  static Type ArgType[] = {TYPE(T_PTR), TYPE(T_CHAR | T_QUAL_CONST), TYPE(T_END)};
  ExprDesc Arg;
  int IsArray;
  int IsPtr;
  int IsByteIndex;
  long ECount;
  unsigned L;

  /* Setup the argument type string */
  ArgType[1].C = GetDefaultChar() | T_QUAL_CONST;

  /* Evaluate the parameter */
  hie1(&Arg);

  /* Check if the argument is an array. If so, remember the element count.
  ** Otherwise set the element count to undefined.
  */
  IsArray = IsTypeArray(Arg.Type);
  if (IsArray) {
    ECount = GetElementCount(Arg.Type);
    if (ECount == FLEXIBLE) {
      /* Treat as unknown */
      ECount = UNSPECIFIED;
    }
    IsPtr = 0;
  } else {
    ECount = UNSPECIFIED;
    IsPtr = IsTypePtr(Arg.Type);
  }

  /* Check if the elements of an array can be addressed by a byte sized
  ** index. This is true if the size of the array is known and less than
  ** 256.
  */
  IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);

  /* Do type conversion */
  TypeConversion(&Arg, ArgType);

  if (IS_Get(&Optimize)) {

    /* If the expression is a literal, and if string literals are read
    ** only, we can calculate the length of the string and remove it
    ** from the literal pool. Otherwise we have to calculate the length
    ** at runtime.
    */
    if (ED_IsLocLiteral(&Arg) && IS_Get(&WritableStrings) == 0) {

      /* Constant string literal */
      ED_MakeConstAbs(Expr, GetLiteralSize(Arg.LVal) - 1, type_size_t);

      /* We don't need the literal any longer */
      ReleaseLiteral(Arg.LVal);

      /* Bail out, no need for further improvements */
      goto ExitPoint;
    }
  }

  if (IS_Get(&InlineStdFuncs)) {

    /* We will inline strlen for arrays with constant addresses, if either
    ** requested on the command line, or the array is smaller than 256,
    ** so the inlining is considered safe.
    */
    if (ED_IsLocConst(&Arg) && IsArray && (IS_Get(&EagerlyInlineFuncs) || IsByteIndex)) {

      /* Generate the strlen code */
      L = GetLocalLabel();
      AddCodeLine("ldy #$FF");
      g_defcodelabel(L);
      AddCodeLine("iny");
      AddCodeLine("ldx %s,y", ED_GetLabelName(&Arg, 0));
      AddCodeLine("bne %s", LocalLabelName(L));
      AddCodeLine("tya");

      /* The function result is an rvalue in the primary register */
      ED_MakeRValExpr(Expr);
      Expr->Type = type_size_t;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    /* We will inline strlen for arrays on the stack, if the array is
    ** completely within the reach of a byte sized index register.
    */
    if (ED_IsLocStack(&Arg) && IsArray && IsByteIndex && (Arg.IVal - StackPtr) + ECount < 256) {

      /* Calculate the true stack offset */
      int Offs = ED_GetStackOffs(&Arg, 0);

      /* Generate the strlen code */
      L = GetLocalLabel();
      AddCodeLine("ldx #$FF");
      AddCodeLine("ldy #$%02X", (unsigned char)(Offs - 1));
      g_defcodelabel(L);
      AddCodeLine("inx");
      AddCodeLine("iny");
      AddCodeLine("lda (sp),y");
      AddCodeLine("bne %s", LocalLabelName(L));
      AddCodeLine("txa");
      AddCodeLine("ldx #$00");

      /* The function result is an rvalue in the primary register */
      ED_MakeRValExpr(Expr);
      Expr->Type = type_size_t;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    /* strlen for a string that is pointed to by a register variable will only
    ** get inlined if requested on the command line, since we cannot know how
    ** big the buffer actually is, so inlining is not always safe.
    */
    if (ED_IsLocRegister(&Arg) && ED_IsLVal(&Arg) && IsPtr && IS_Get(&EagerlyInlineFuncs)) {

      /* Generate the strlen code */
      L = GetLocalLabel();
      AddCodeLine("ldy #$FF");
      g_defcodelabel(L);
      AddCodeLine("iny");
      AddCodeLine("lda (%s),y", ED_GetLabelName(&Arg, 0));
      AddCodeLine("bne %s", LocalLabelName(L));
      AddCodeLine("tax");
      AddCodeLine("tya");

      /* The function result is an rvalue in the primary register */
      ED_MakeRValExpr(Expr);
      Expr->Type = type_size_t;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }

    /* Last check: We will inline a generic strlen routine if inlining was
    ** requested on the command line, and the code size factor is more than
    ** 400 (code is 13 bytes vs. 3 for a jsr call).
    */
    if (IS_Get(&CodeSizeFactor) > 400 && IS_Get(&EagerlyInlineFuncs)) {

      /* Load the expression into the primary */
      LoadExpr(CF_NONE, &Arg);

      /* Inline the function */
      L = GetLocalLabel();
      AddCodeLine("sta ptr1");
      AddCodeLine("stx ptr1+1");
      AddCodeLine("ldy #$FF");
      g_defcodelabel(L);
      AddCodeLine("iny");
      AddCodeLine("lda (ptr1),y");
      AddCodeLine("bne %s", LocalLabelName(L));
      AddCodeLine("tax");
      AddCodeLine("tya");

      /* The function result is an rvalue in the primary register */
      ED_MakeRValExpr(Expr);
      Expr->Type = type_size_t;

      /* Bail out, no need for further processing */
      goto ExitPoint;
    }
  }

  /* Load the expression into the primary */
  LoadExpr(CF_NONE, &Arg);

  /* Call the strlen function */
  AddCodeLine("jsr _%s", Func_strlen);

  /* The function result is an rvalue in the primary register */
  ED_MakeRValExpr(Expr);
  Expr->Type = type_size_t;

ExitPoint:
  /* We expect the closing brace */
  ConsumeRParen();
}

/*****************************************************************************/
/*                                   Code                                    */
/*****************************************************************************/

int FindStdFunc(const char *Name)
/* Determine if the given function is a known standard function that may be
** called in a special way. If so, return the index, otherwise return -1.
*/
{
  /* Look into the table for known names */
  struct StdFuncDesc *D = bsearch(Name, StdFuncs, FUNC_COUNT, sizeof(StdFuncs[0]), CmpFunc);

  /* Return the function index or -1 */
  if (D == 0) {
    return -1;
  } else {
    return D - StdFuncs;
  }
}

void HandleStdFunc(int Index, FuncDesc *F, ExprDesc *lval)
/* Generate code for a known standard function. */
{
  struct StdFuncDesc *D;

  /* Get a pointer to the table entry */
  CHECK(Index >= 0 && Index < (int)FUNC_COUNT);
  D = StdFuncs + Index;

  /* Call the handler function */
  D->Handler(F, lval);
}
